package org.example.thread.chapterVIII;

import java.util.Map;
import java.util.concurrent.*;

/**
 * 同步屏障 CyclicBarrier ： 让一组线程到达一个屏障（也可以叫同步点）时被阻塞，
 * 直接最后一个线程到达屏障时，屏障才会开门，所有被屏障拦截的线程才会继续执行。
 * <p>
 * CyclicBarrier 可以用于多线程计算数据，最后合并计算结果的场景
 * 例如 一个Excel保存了用户所有银行流水，每个Sheet保存一个账户近一年的每笔银行流水，现在需要统计用户的日均银行流水。
 * 先用多线程处理每个Sheet里的银行流水，都执行完之后，得到每个sheet的日均银行流水。
 * 最后，再用barrierAction 用这些线程的计算结果，计算出整个Excel的日均银行流水。
 */
public class BankWaterService implements Runnable {

    /**
     * 创建4个屏障，处理完之后执行当前类的 run()
     */
    private CyclicBarrier cyclicBarrier = new CyclicBarrier(4, this);
    /**
     * 假设只有4个sheet,所以只启动4个线程
     */
    private Executor executor = Executors.newFixedThreadPool(4);

    private ConcurrentHashMap<String, Integer> sheetBankWaterCount = new ConcurrentHashMap<>();

    private void count() {
        for (int i = 0; i < 4; i++) {
            executor.execute(() -> {
                // 计算当前sheet的银行流水数据
                sheetBankWaterCount.put(Thread.currentThread().getName(), 1);
                // 银行流水计算完成，插入一个屏障
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            });
        }
    }

    @Override
    public void run() {
        int result = 0;
        // 汇总每个sheet计算出的结果
        for (Map.Entry<String, Integer> entry : sheetBankWaterCount.entrySet()) {
            result += entry.getValue();
        }
        // 输出结果
        sheetBankWaterCount.put("result", result);
        System.out.println(result);
    }

    public static void main(String[] args) {
        BankWaterService service = new BankWaterService();
        service.count();
    }
}
